package com.lemon.reggie.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lemon.reggie.common.BusinessException;
import com.lemon.reggie.pojo.dto.AddSetMealDTO;
import com.lemon.reggie.pojo.dto.DishDTO;
import com.lemon.reggie.pojo.dto.EditSetMealDTO;
import com.lemon.reggie.pojo.entity.Category;
import com.lemon.reggie.pojo.entity.Setmeal;
import com.lemon.reggie.mapper.SetmealMapper;
import com.lemon.reggie.pojo.entity.SetmealDish;
import com.lemon.reggie.pojo.vo.GetSetMealVO;
import com.lemon.reggie.pojo.vo.SetMealVO;
import com.lemon.reggie.service.CategoryService;
import com.lemon.reggie.service.SetmealDishService;
import com.lemon.reggie.service.SetmealService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 套餐 服务实现类
 * </p>
 *
 * @author Lemon
 * @since 2022-05-23
 */
@Service
@Slf4j
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {

    @Resource
    private SetmealDishService setmealDishService;
    @Resource
    private CategoryService categoryService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 添加套餐
     * @param addSetMealDTO
     * @return void
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSetMeal(AddSetMealDTO addSetMealDTO) {
        //1.保存套餐
        Setmeal setmeal = Setmeal.builder()
                .categoryId(addSetMealDTO.getCategoryId())
                .code(addSetMealDTO.getCode())
                .name(addSetMealDTO.getName())
                .price(addSetMealDTO.getPrice())
                .image(addSetMealDTO.getImage())
                .status(addSetMealDTO.getStatus())
                .description(addSetMealDTO.getDescription())
                .build();
        this.save(setmeal);

        //2.保存套餐选中的菜品
        List<DishDTO> dishDTOList = addSetMealDTO.getSetmealDishes();
        if (CollUtil.isNotEmpty(dishDTOList)) {
            setmealDishService.saveBatch(this.getSetMealDishList(dishDTOList, setmeal));

            /*List<SetmealDish> setmealDishList = dishDTOList.stream().map(dishDTO -> {
                SetmealDish setmealDish = new SetmealDish();
                BeanUtil.copyProperties(dishDTO, setmealDish);
                setmealDish.setSetmealId(String.valueOf(setmeal.getId()));
                return setmealDish;
            }).collect(Collectors.toList());
            setmealDishService.saveBatch(setmealDishList);*/
        }
        //3. 删除redis中当前分类下的套餐
        final String setMealKey = "setMeal_" + addSetMealDTO.getCategoryId();
        redisTemplate.delete(setMealKey);
    }

    /**
     * 套餐分页
     * @param currentPage
     * @param pageSize
     * @param name
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.lemon.reggie.pojo.vo.SetMealVO>
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    public IPage<SetMealVO> pageSetMeal(Long currentPage, Long pageSize, String name) {
        IPage<Setmeal> page = new Page<>(currentPage, pageSize);
        IPage<Setmeal> setMealPage = this.page(page, new LambdaQueryWrapper<Setmeal>()
                .like(StrUtil.isNotBlank(name), Setmeal::getName, name)
                .orderByDesc(Setmeal::getUpdateTime));

        List<Setmeal> setMealList = setMealPage.getRecords();
        List<SetMealVO> setMealVOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(setMealList)) {
            setMealVOList = setMealList.stream().map(setmeal -> {
                SetMealVO setMealVO = new SetMealVO();
                BeanUtil.copyProperties(setmeal, setMealVO);
                setMealVO.setCategoryName(this.getCategoryName(setmeal.getCategoryId()));
                return setMealVO;
            }).collect(Collectors.toList());
        }

        IPage<SetMealVO> voPage = new Page<>(currentPage, pageSize);
        BeanUtil.copyProperties(setMealPage,voPage,"records");
        voPage.setRecords(setMealVOList);
        return voPage;
    }

    /**
     * 停售/起售
     * @param status
     * @param ids
     * @return void
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void statusSetMeal(Integer status, List<Long> ids) {
        List<Setmeal> setMealList = this.list(new LambdaQueryWrapper<Setmeal>().in(Setmeal::getId, ids));
        if (CollUtil.isNotEmpty(setMealList)) {
            setMealList.forEach(setmeal -> setmeal.setStatus(status));
            this.updateBatchById(setMealList);

            //删除所有redis中存在的套餐
            Set<String> setMealKeys = redisTemplate.keys("setMeal_*");
            if (CollUtil.isNotEmpty(setMealKeys)) {
                redisTemplate.delete(setMealKeys);
            }
        }
    }

    /**
     * 批量删除
     * @param ids
     * @return void
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    /**
     * CacheEvict：清理指定缓存
     * value：缓存的名称，每个缓存名称下面可以有多个key
     * key：缓存的key
     * allEntries：是否删除当前缓存名称下的所有的key,默认是false
     */
    @CacheEvict(value = "setMeal", allEntries = true)
    public String deleteSetMeal(List<Long> ids) {
        //正在起售的套餐不能删除
        List<Setmeal> notList = this.list(new LambdaQueryWrapper<Setmeal>()
                .eq(Setmeal::getStatus, 1)
                .in(Setmeal::getId, ids));
        String notNames = null;
        List<Long> deleteIds = null;
        if (CollUtil.isNotEmpty(notList)) {
            notNames = notList.stream().map(Setmeal::getName)
                    .collect(Collectors.joining("；"));

            List<Long> notIds = notList.stream().map(Setmeal::getId).collect(Collectors.toList());
            Collection<Long> disjunction = CollUtil.disjunction(ids, notIds);
            deleteIds = new ArrayList<>(disjunction);
        } else {
            deleteIds = new ArrayList<>(ids);
        }

        if (CollUtil.isNotEmpty(deleteIds)) {
            this.removeByIds(deleteIds);
            this.deleteSetMealDish(deleteIds);
        }

        //删除所有redis中存在的套餐
        Set<String> setMealKeys = redisTemplate.keys("setMeal_*");
        if (CollUtil.isNotEmpty(setMealKeys)) {
            redisTemplate.delete(setMealKeys);
        }

        return notNames;

        /*List<SetmealDish> setmealDishList = setmealDishService.list(new LambdaQueryWrapper<SetmealDish>()
                .in(SetmealDish::getSetmealId, ids));
        if (CollUtil.isNotEmpty(setmealDishList)) {
            List<Long> idList = setmealDishList.stream().map(SetmealDish::getId).collect(Collectors.toList());
            setmealDishService.removeByIds(idList);
        }*/
    }

    public static void main(String[] args) {
        List<Long> notIds = Arrays.asList(1L, 2L, 3L);
        List<Long> totalIds = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L);
        Collection<Long> disjunction = CollUtil.disjunction(notIds, totalIds);
        List<Long> disList = new ArrayList<>(disjunction);

        System.out.println(disList);
    }

    /**
     * 根据Id获取套餐信息
     * @param id
     * @return com.lemon.reggie.pojo.vo.GetSetMealVO
     * @author Guannankun
     * @since 2022/8/18
     */
    @Override
    public GetSetMealVO getSetMeal(Long id) {
        Setmeal setmeal = this.getById(id);
        if (Objects.isNull(setmeal)) {
            throw new BusinessException("无当前套餐信息！");
        }

        GetSetMealVO getSetMealVO = new GetSetMealVO();
        BeanUtil.copyProperties(setmeal, getSetMealVO);

        List<SetmealDish> setmealDishList = setmealDishService.list(new LambdaQueryWrapper<SetmealDish>()
                .eq(SetmealDish::getSetmealId, id));
        if (CollUtil.isNotEmpty(setmealDishList)) {
            List<DishDTO> dishDTOList = setmealDishList.stream().map(setmealDish -> DishDTO.builder()
                    .name(setmealDish.getName())
                    .copies(setmealDish.getCopies())
                    .dishId(setmealDish.getDishId())
                    .price(setmealDish.getPrice())
                    .build())
                    .collect(Collectors.toList());
            getSetMealVO.setSetmealDishes(dishDTOList);
        }

        return getSetMealVO;
    }

    /**
     * 修改套餐
     * @param editSetMealDTO
     * @return void
     * @author Guannankun
     * @since 2022/8/18
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editSetMeal(EditSetMealDTO editSetMealDTO) {
        Setmeal setmeal = this.getById(editSetMealDTO.getId());
        if (Objects.isNull(setmeal)) {
            throw new BusinessException("无当前套餐信息！");
        }

        BeanUtil.copyProperties(editSetMealDTO, setmeal);
        this.updateById(setmeal);

        this.deleteSetMealDish(Collections.singletonList(editSetMealDTO.getId()));
        List<DishDTO> dishDTOList = editSetMealDTO.getSetmealDishes();
        if (CollUtil.isNotEmpty(dishDTOList)) {
            setmealDishService.saveBatch(this.getSetMealDishList(dishDTOList, setmeal));
        }

        //3. 删除redis中当前分类下的套餐
        final String setMealKey = "setMeal_" + editSetMealDTO.getCategoryId();
        redisTemplate.delete(setMealKey);
    }

    /**
     * 移动端根据分类id获取套餐信息
     * @param categoryId
     * @param status
     * @return java.util.List<com.lemon.reggie.pojo.vo.SetMealVO>
     * @author Guannankun
     * @since 2022/8/22
     */
    /**
     * Cacheable：在方法执行前spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据；若没有数据，调用方法并将方法返回值放到缓存中
     * value：缓存的名称，每个缓存名称下面可以有多个key
     * key：缓存的key
     * condition：条件，满足条件时才缓存数据
     * unless：满足条件则不缓存
     * sync 是否加锁
     */
    @Override
    //高并发下，可以加锁，防止缓存击穿，全部打在DB上，sync = true  默认为false
    //@Cacheable(value = "setMeal", key = "#categoryId",sync = true)
    @Cacheable(value = "setMeal", key = "#categoryId")
    public List<SetMealVO> listSetMeal(Long categoryId, Integer status) {
        List<SetMealVO> setMealVOS = null;
        /*final String setMealKey = "setMeal_" + categoryId;

        try {
            setMealVOS = (List<SetMealVO>) redisTemplate.opsForValue().get(setMealKey);
            if (CollUtil.isNotEmpty(setMealVOS)) {
                return setMealVOS;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("redis异常,{}", ExceptionUtils.getStackTrace(e));
        }*/
        List<Setmeal> setMealList = this.list(new LambdaQueryWrapper<Setmeal>()
                .eq(Setmeal::getCategoryId, categoryId)
                .eq(Setmeal::getStatus, 1)
                .orderByDesc(Setmeal::getUpdateTime));

        if (CollUtil.isNotEmpty(setMealList)) {
            setMealVOS = this.setMeal2VO(setMealList);

            /*try {
                redisTemplate.opsForValue().set(setMealKey, setMealVOS, 1, TimeUnit.HOURS);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("redis异常,{}", ExceptionUtils.getStackTrace(e));
            }*/

            return setMealVOS;
        }

        return Collections.emptyList();
    }

    /**
     * 将setMeal-->SetMealVO
     * @param setMealList
     * @return java.util.List<com.lemon.reggie.pojo.vo.SetMealVO>
     * @author Guannankun
     * @since 2022/8/22
     */
    private List<SetMealVO> setMeal2VO(List<Setmeal> setMealList) {
        return setMealList.stream().map(setmeal -> {
            SetMealVO setMealVO = new SetMealVO();
            BeanUtil.copyProperties(setmeal, setMealVO);
            setMealVO.setCategoryName(this.getCategoryName(setmeal.getCategoryId()));
            setMealVO.setSetmealDishes(this.getDishDTOS(String.valueOf(setmeal.getId())));
            return setMealVO;
        }).collect(Collectors.toList());
    }

    /**
     * 根据套餐id获取套餐菜品集合
     * @param setmealId
     * @return java.util.List<com.lemon.reggie.pojo.dto.DishDTO>
     * @author Guannankun
     * @since 2022/8/22
     */
    private List<DishDTO> getDishDTOS(String setmealId) {
        List<SetmealDish> setmealDishList = setmealDishService.list(new LambdaQueryWrapper<SetmealDish>()
                .eq(SetmealDish::getSetmealId, setmealId));
        if (CollUtil.isNotEmpty(setmealDishList)) {
            return setmealDishList.stream().map(setmealDish -> DishDTO.builder()
                    .dishId(setmealDish.getDishId())
                    .name(setmealDish.getName())
                    .price(setmealDish.getPrice())
                    .copies(setmealDish.getCopies())
                    .build())
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 获取套餐分类名称
     * @param categoryId
     * @return java.lang.String
     * @author Guannankun
     * @since 2022/8/17
     */
    private String getCategoryName(Long categoryId) {
        Category one = categoryService.getOne(new LambdaQueryWrapper<Category>()
                .eq(Category::getType, 2)
                .eq(Category::getId, categoryId));
        return one.getName();
    }

    /**
     * 根据套餐ids,获取菜品集合并删除
     * @param ids
     * @return void
     * @author Guannankun
     * @since 2022/8/18
     */
    private void deleteSetMealDish(List<Long> ids) {
        List<SetmealDish> setmealDishList = setmealDishService.list(new LambdaQueryWrapper<SetmealDish>()
                .in(SetmealDish::getSetmealId, ids));
        if (CollUtil.isNotEmpty(setmealDishList)) {
            List<Long> idList = setmealDishList.stream().map(SetmealDish::getId).collect(Collectors.toList());
            setmealDishService.removeByIds(idList);
        }
    }

    /**
     * dishDTO2SetmealDish
     * @param dishDTOList
     * @param setmeal
     * @return java.util.List<com.lemon.reggie.pojo.entity.SetmealDish>
     * @author Guannankun
     * @since 2022/8/18
     */
    private List<SetmealDish> getSetMealDishList(List<DishDTO> dishDTOList, Setmeal setmeal) {
        return dishDTOList.stream().map(dishDTO -> {
            SetmealDish setmealDish = new SetmealDish();
            BeanUtil.copyProperties(dishDTO, setmealDish);
            setmealDish.setSetmealId(String.valueOf(setmeal.getId()));
            return setmealDish;
        }).collect(Collectors.toList());
    }
}
